//
// Created by ASUS on 2024/12/12/星期四.
//
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>

using namespace std;

// 基数排序，用于构建后缀数组
void radix_sort(vector<int>& sa, vector<int>& rk, vector<int>& px, vector<int>& id, int n, int m) {
	// 对第二关键字排序
	fill(id.begin(), id.end(), 0);
	for (int i = 1; i <= n; ++i) id[px[i]]++;
	for (int i = 1; i <= m; ++i) id[i] += id[i - 1];
	for (int i = n; i >= 1; --i) sa[id[px[i]]--] = i;

	// 根据第二关键字和第一关键字综合排序
	fill(id.begin(), id.end(), 0);
	for (int i = 1; i <= n; ++i) {
		if (i == 1 || rk[sa[i]]!= rk[sa[i - 1]] || rk[sa[i] + 1]!= rk[sa[i - 1] + 1]) {
			id[sa[i]] = i;
		} else {
			id[sa[i]] = id[sa[i - 1]];
		}
	}
	for (int i = 1; i <= n; ++i) rk[i] = id[i];
}

// 构建后缀数组
vector<int> build_suffix_array(const string& s) {
	int n = s.size();
	int m = 128;  // 假设字符集范围，可按实际调整
	vector<int> sa(n + 1), rk(n + 1), oldrk(n + 1), px(n + 1), id(n + 1);
	// 初始化排名数组rk
	for (int i = 0; i < n; ++i) {
		rk[i + 1] = s[i];
		px[i + 1] = i + 1;
	}

	// 倍增法结合基数排序构建后缀数组
	for (int w = 1; w < n; w <<= 1) {
		for (int i = 1; i <= n; ++i) {
			px[i] = (i + w <= n)? rk[i + w] : 0;
		}
		radix_sort(sa, rk, px, id, n, m);
	}
	return sa;
}

// 计算height数组
vector<int> get_height(const string& s, const vector<int>& sa, const vector<int>& rk) {
	int n = s.size();
	vector<int> height(n + 1);
	int k = 0;
	for (int i = 0; i < n; ++i) {
		if (rk[i] == 0) {
			// 排名为0的后缀（这里第一个后缀排名为0，和之前代码逻辑稍不同，需注意）
			height[rk[i]] = 0;
			continue;
		}
		int j = sa[rk[i] - 1];
		if (k > 0) k--;
		while (i + k < n && j + k < n && s[i + k] == s[j + k]) k++;
		height[rk[i]] = k;
	}
	return height;
}

int main() {
	string s;
	cin >> s;
	vector<int> sa = build_suffix_array(s);
	vector<int> rk(s.size() + 1);
	for (int i = 0; i < sa.size(); ++i) {
		rk[sa[i]] = i;
	}
	vector<int> height = get_height(s, sa, rk);
	for (int i = 0; i < height.size(); ++i) {
		cout << height[i] << " ";
	}
	cout << endl;
	return 0;
}